package com.zws.cucumber.bootstrap.cass;

import org.apache.commons.io.FileUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import static com.zws.cucumber.bootstrap.ZwsCassBootstrap.BACKDOOR_PORT;
import static com.zws.cucumber.bootstrap.ZwsCassBootstrap.CASSANDRA_PORT;
import static com.zws.cucumber.bootstrap.cass.ZwsBootstrapUtil.*;

public final class ZwsStandaloneCassWslBootstrap extends ZwsAbstractCassBootstrap {

    public static final String PATTERN = "classpath*:bootstrap-scripts/cassandra/%s";
    public static final String PREPARE_ENV_FILE = "prepareEnv_wsl.sh";
    public static final String PREPARE_CASSANDRA_FILE = "prepareCassandra_wsl.sh";
    public static final String PATTERN_PREPARE_ENV = PATTERN.formatted(PREPARE_ENV_FILE);
    public static final String PATTERN_PREPARE_CASSANDRA = PATTERN.formatted(PREPARE_CASSANDRA_FILE);
    public static final String PARENT_DIRECTORY = "/tmp/zws-test-cassandra3";

    private String directory = null;

    @Override
    public int getCassandraPort() {
        return CASSANDRA_PORT;
    }

    @Override
    public int getBackdoorPort() {
        return BACKDOOR_PORT;
    }

    @Override
    public String getPathOfSystemLogs() {
        return PARENT_DIRECTORY + "/" + directory + "/logs/system.log";
    }

    @Override
    public void prepareAndStartCassandra0() throws Exception {
        mkdir();
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        prepareCassandra(resolver);
        startCassandra(resolver);
    }

    private void mkdir() {
        try {
            FileUtils.forceMkdir(FileUtils.getFile(PARENT_DIRECTORY));
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    private String readDirectory(PathMatchingResourcePatternResolver resolver) throws IOException {
        Resource res = resolver.getResources(PATTERN_PREPARE_ENV)[0];
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(res.getInputStream()))) {
            String line = null;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("export STANDALONE_CASSANDRA_DIR=")) {
                    return line.substring("export STANDALONE_CASSANDRA_DIR=".length());
                }
            }
        }
        return null;
    }

    private void prepareCassandra(PathMatchingResourcePatternResolver resolver) throws IOException, InterruptedException {
        ZwsBootstrapUtil.extractFile(resolver, PATTERN_PREPARE_ENV, PARENT_DIRECTORY + "/" + PREPARE_ENV_FILE);
        ZwsBootstrapUtil.extractFile(resolver, PATTERN_PREPARE_CASSANDRA, PARENT_DIRECTORY + "/" + PREPARE_CASSANDRA_FILE);

        new File(PARENT_DIRECTORY + "/" + PREPARE_ENV_FILE).setExecutable(true, false);
        new File(PARENT_DIRECTORY + "/" + PREPARE_CASSANDRA_FILE).setExecutable(true, false);

        String[] envs = getPrepareCassandraEnvsBuilder().toArray();
        Process p1 = Runtime.getRuntime().exec(new String[] { "/bin/bash", PREPARE_CASSANDRA_FILE, "-f" }, envs, new File(PARENT_DIRECTORY));
        p1.getInputStream().transferTo(System.out);
        p1.getErrorStream().transferTo(System.err);
        int exitCode = p1.waitFor();
        System.out.println("exitCode: " + exitCode);
        if (exitCode != 0) {
            throw new RuntimeException("Failed to prepare Cassandra, exitCode: %d".formatted(exitCode));
        }
    }

    private void startCassandra(PathMatchingResourcePatternResolver resolver) throws IOException, InterruptedException {
        startTimer();
        directory = readDirectory(resolver);

        FileUtils.deleteDirectory(new File(PARENT_DIRECTORY + "/" + directory + "/logs"));
        FileUtils.deleteDirectory(new File(PARENT_DIRECTORY + "/" + directory + "/data"));

        String[] envs = getStartCassandraEnvsBuilder()
                .withEnv("cassandra_parms", String.join(
                        " ",
                        "-Dcassandra-foreground=yes",
                        "-Dcassandra.unsafesystem=true",
                        "-Dcassandra.native.epoll.enabled=false",
                        "-Dcassandra.custom_query_handler_class=zws.common.TestQueryHandler"))
                .toArray();

        Process proc = Runtime.getRuntime().exec(
                new String[] { "/bin/bash", "cassandra" },
                envs,
                new File(PARENT_DIRECTORY + "/" + directory + "/" + "bin"));
        handleStdoutAndStderr(proc);

        String pid = waitAndGetPid();
        makeKillCassandraScript(pid);
        println("standalone cassandra: started, elapsed: %dms".formatted(elapsed()));
    }

    private void makeKillCassandraScript(String pid) {
        final String cmd = "kill " + pid;
        final String filepath = PARENT_DIRECTORY + "/kill-cassandra.sh";
        try { ZwsBootstrapUtil.makeFile(filepath, cmd); } catch (Exception e) { e.printStackTrace(); }
    }

}
